package com.dqv5.shorturl.service;

import com.dqv5.shorturl.config.Constants;
import com.dqv5.shorturl.exception.CommonShortException;
import com.dqv5.shorturl.util.RadixUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author duq
 * @date 2020/11/22
 */
@Service
@Slf4j
public class InitService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    /**
     * 初始化短链接池，往redis中存入随机打乱的一组固定长度的短链接集合，暂只支持到长度为5
     * length=1时，要初始化 1 ~ Z (62进制) ，即  62^0 ~ 62^1-1（10进制）
     * length=2时，要初始化 10 ~ ZZ (62进制) ，即 62^1 ~ 62^2-1（10进制）
     * length=3时，要初始化 100 ~ ZZZ (62进制) ，即  62^2 ~ 62^3-1（10进制）
     * length=4时，要初始化 1000 ~ ZZZZ (62进制) ，即  62^3 ~ 62^4-1（10进制）
     * length=5时，要初始化 10000 ~ ZZZZZ (62进制) ，即  62^4 ~ 62^5-1（10进制）
     *
     * @param codeLength 字符串长度
     */
    public void initShortCodePool(int codeLength) {
        if (codeLength < 1 || codeLength > 5) {
            throw new CommonShortException("参数不正确，长度应为1-5的数字！");
        }
        if (codeLength < 5) {
            List<String> list = this.randomCodeList(codeLength);
            this.redisPushAllCache(Constants.SHORTURL_CODE_LIST_KEY, list);
        } else {
            // 字符串长度大于等于5时，上面的方法效率较差，所以采取前四位+第五位分开计算的方法。
            // 前四位按照上面的方法打乱排序，再加一个第五位，范围[0-Z]共62个字符
            List<String> singleList = new ArrayList<>();
            for (int i = 0; i <= 61; i++) {
                String code = RadixUtil.from10(String.valueOf(i), 62);
                singleList.add(code);
            }
            this.randomSingleChar(singleList);
            List<String> subList = randomCodeList(4);
            for (String s : singleList) {
                List<String> tempList = new ArrayList<>();
                for (String subCode : subList) {
                    tempList.add(subCode + s);
                }
                this.redisPushAllCache(Constants.SHORTURL_CODE_LIST_KEY, tempList);
            }
        }
        Long size = redisTemplate.opsForList().size(Constants.SHORTURL_CODE_LIST_KEY);
        log.debug("redis中共有元素{}个", size);
    }


    private List<String> randomCodeList(int codeLength) {
        if (codeLength < 1) {
            throw new CommonShortException("参数不正确，长度应大于0的整数！");
        }
        log.info("开始正常排序的集合...");
        List<String> list = new ArrayList<>();
        int start = (int) Math.pow(62, (codeLength - 1));
        int end = (int) Math.pow(62, codeLength) - 1;
        int arrayLength = end - start + 1;
        for (int i = 0; i < arrayLength; i++) {
            String code = RadixUtil.from10(String.valueOf(start + i), 62);
            list.add(code);
        }
        log.info("顺序插入完成，开始打乱排序...");
        // 打乱排序，随机生成一个数组下标，将该元素与当前循环的元素进行交换
        Random random = new Random();
        for (int i = 0; i < arrayLength; i++) {
            int index = random.nextInt(arrayLength - i) + i;
            if (index != i) {
                String temp = list.get(index);
                list.set(index, list.get(i));
                list.set(i, temp);
            }
        }
        log.info("打乱排序完成...");
        return list;
    }


    private List<String> randomSingleChar(List<String> list) {
        // 打乱排序，随机生成一个数组下标，将该元素与当前循环的元素进行交换
        Random random = new Random();
        for (int i = 0; i < list.size(); i++) {
            int index = random.nextInt(list.size() - i) + i;
            if (index != i) {
                String temp = list.get(index);
                list.set(index, list.get(i));
                list.set(i, temp);
            }
        }
        return list;
    }


    private void redisPushAllCache(String redisKey, List<String> list) {
        if (list == null || list.isEmpty()) {
            return;
        }
        log.info("准备往redis中插入共{}条数据", list.size());
        int bufferSize = 1000000;
        List<String> buffer = new ArrayList<>();
        for (int i = 0; i < list.size(); i += bufferSize) {
            if (i + bufferSize < list.size()) {
                buffer = list.subList(i, i + bufferSize);
            } else {
                buffer = list.subList(i, list.size());
            }
            redisTemplate.opsForList().rightPushAll(redisKey, buffer);
            log.debug("已存入redis{}条数据", buffer.size());
        }
    }
}
